package asruby.asupport {

import asruby.Ruby;
import asruby.exceptions.JumpException;
import asruby.exceptions.RaiseException;
import asruby.lang.RubyArray;
import asruby.lang.RubyBoolean;
import asruby.lang.RubyClass;
import asruby.lang.RubyException;
import asruby.lang.RubyFixnum;
import asruby.lang.RubyModule;
import asruby.lang.RubyObject;
import asruby.lang.RubyString;
import asruby.lang.RubySymbol;
import asruby.parser.BlockStaticScope;
import asruby.parser.StaticScope;
import asruby.runtime.Arity;
import asruby.runtime.Block;
import asruby.runtime.CallType;
import asruby.runtime.CompiledBlock;
import asruby.runtime.CompiledBlockCallback;
import asruby.runtime.CompiledBlockLight;
import asruby.runtime.IRubyObject;
import asruby.runtime.MethodIndex;
import asruby.runtime.ThreadContext;
import asruby.runtime.Visibility;
import asruby.runtime.methods.DynamicMethod;
/**
 * Helper methods which are called by the compiler.  Note: These will show no consumers, but
 * generated code does call these so don't remove them thinking they are dead code. 
 *
 */
public class RuntimeHelpers {
	
    public static function createBlock(context:ThreadContext, self:IRubyObject, arity:int, 
            staticScopeNames:Array, callback:CompiledBlockCallback, hasMultipleArgsHead:Boolean, argsNodeType:int, light:Boolean):Block  {
        var staticScope: StaticScope =  
            new BlockStaticScope(context.getCurrentScope().getStaticScope(), staticScopeNames);
        staticScope.determineModule();
        
        if (light) {
            return CompiledBlockLight.newCompiledClosureLight2(
                    context,
                    self,
                    Arity.createArity(arity),
                    staticScope,
                    callback,
                    hasMultipleArgsHead,
                    argsNodeType);
        } else {
            return CompiledBlock.newCompiledClosure2(
                    context,
                    self,
                    Arity.createArity(arity),
                    staticScope,
                    callback,
                    hasMultipleArgsHead,
                    argsNodeType);
        }
    }
    
/*    
    public static function runBeginBlock(context:ThreadContext, self:IRubyObject, staticScopeNames:Array, callback:CompiledBlockCallback):IRubyObject{
        var staticScope: StaticScope =  
            new BlockStaticScope(context.getCurrentScope().getStaticScope(), staticScopeNames);
        staticScope.determineModule();
        
        context.preScopedBody(DynamicScope.newDynamicScope(staticScope, context.getCurrentScope()));
        
        var block: Block =  CompiledBlock.newCompiledClosure2(context, self, Arity.createArity(0), staticScope, callback, false, Block.ZERO_ARGS);
        
        block.yield(context, null);
        
        context.postScopedBody();
        
        return context.getRuntime().getNil();
    }
    
    public static function createSharedScopeBlock(context:ThreadContext, self:IRubyObject, arity:int, 
            callback:CompiledBlockCallback, hasMultipleArgsHead:Boolean, argsNodeType:int):Block {
        
        return CompiledSharedScopeBlock.newCompiledSharedScopeClosure(context, self, Arity.createArity(arity), 
                context.getCurrentScope(), callback, hasMultipleArgsHead, argsNodeType);
    } 
    
    public static function def(context:ThreadContext, self:IRubyObject, scriptObject:Object, name:String, javaName:String, scopeNames:Array,
            arity:int, required:int, optional:int, rest:int, callConfig:CallConfiguration ):IRubyObject {
        var compiledClass: Class =  scriptObject.getClass();
        var runtime: Ruby =  context.getRuntime();
        
        var containingClass: RubyModule =  context.getRubyClass();
        var visibility: Visibility =  context.getCurrentVisibility();
        
        if (containingClass == null) {
            throw runtime.newTypeError("No class to add method.");
        }
        
        if (containingClass == runtime.getObject() && name == "initialize") {
            runtime.getWarnings().warn(RubyWarnings.REDEFINING_DANGEROUS, "redefining Object#initialize may cause infinite loop", "Object#initialize");
        }

        if (name == "__id__" || name =="__send__" ) {
            runtime.getWarnings().warn(RubyWarnings.REDEFINING_DANGEROUS, "redefining `" + name + "' may cause serious problem", name); 
        }

        var scope: StaticScope =  new LocalStaticScope(context.getCurrentScope().getStaticScope(), scopeNames);
        scope.determineModule();
        scope.setArities(required, optional, rest);
        
        var factory: MethodFactory =  MethodFactory.createFactory(compiledClass.getClassLoader());
        var method:DynamicMethod;
        
        if (name == "initialize" || visibility == Visibility.MODULE_FUNCTION) {
            method = factory.getCompiledMethod(containingClass, javaName, 
                    Arity.createArity(arity), Visibility.PRIVATE, scope, scriptObject, callConfig);
        } else {
            method = factory.getCompiledMethod(containingClass, javaName, 
                    Arity.createArity(arity), visibility, scope, scriptObject, callConfig);
        }
        
        containingClass.addMethod(name, method);
        
        if (visibility == Visibility.MODULE_FUNCTION) {
            containingClass.getSingletonClass().addMethod(name,
                    new WrapperMethod(containingClass.getSingletonClass(), method,
                    Visibility.PUBLIC));
            containingClass.callMethod(context, "singleton_method_added", runtime.fastNewSymbol(name));
        }
        
        // 'class << state.self' and 'class << obj' uses defn as opposed to defs
        if (containingClass.isSingleton()) {
            (containingClass).getAttached().callMethod(
                    context, "singleton_method_added", runtime.fastNewSymbol(name));
        } else {
            containingClass.callMethod(context, "method_added", runtime.fastNewSymbol(name));
        }
        
        return runtime.getNil();
    }
    
    public static function defs(context:ThreadContext, self:IRubyObject,receiver:IRubyObject , scriptObject:Object, name:String, javaName:String, scopeNames:Array,
            arity:int, required:int, optional:int, rest:int, callConfig:CallConfiguration ):IRubyObject {
        var compiledClass: Class =  scriptObject.getClass();
        var runtime: Ruby =  context.getRuntime();
        
        if (runtime.getSafeLevel() >= 4 && !receiver.isTaint()) {
            throw runtime.newSecurityError("Insecure; can't define singleton method.");
        }

        if (receiver instanceof RubyFixnum || receiver instanceof RubySymbol) {
          throw runtime.newTypeError("can't define singleton method \"" + name
          + "\" for " + receiver.getMetaClass().getBaseName());
        }

        if (receiver.isFrozen()) throw runtime.newFrozenError("object");

        var rubyClass: RubyClass =  receiver.getSingletonClass();

        if (runtime.getSafeLevel() >= 4 && rubyClass.getMethods().get(name) != null) {
            throw runtime.newSecurityError("redefining method prohibited.");
        }
        
        var scope: StaticScope =  new LocalStaticScope(context.getCurrentScope().getStaticScope(), scopeNames);
        scope.determineModule();
        scope.setArities(required, optional, rest);
        
        var factory: MethodFactory =  MethodFactory.createFactory(compiledClass.getClassLoader());
        var method:DynamicMethod;
        
        method = factory.getCompiledMethod(rubyClass, javaName, 
                Arity.createArity(arity), Visibility.PUBLIC, scope, scriptObject, callConfig);
        
        rubyClass.addMethod(name, method);
        receiver.callMethod(context, "singleton_method_added", runtime.fastNewSymbol(name));
        
        return runtime.getNil();
    }
    */
    public static function getSingletonClass(runtime:Ruby, receiver:IRubyObject):RubyClass{
        if (receiver instanceof RubyFixnum || receiver instanceof RubySymbol) {
            throw runtime.newTypeError("no virtual class for " + receiver.getMetaClass().getBaseName());
        } else {
            if (runtime.getSafeLevel() >= 4 && !receiver.isTaint()) {
                throw runtime.newSecurityError("Insecure: can't extend object.");
            }

            return receiver.getSingletonClass();
        }
    }

    public static function doAttrAssign(receiver:IRubyObject , args:Array, 
            context:ThreadContext, name:String, caller:IRubyObject, callType:CallType , block:Block):IRubyObject {
        if (receiver == caller) callType = CallType.VARIABLE;
        
        return compilerCallMethod(context, receiver, name, args, caller, callType, block);
    }
    
    public static function doAttrAssignIndexed(receiver:IRubyObject , args:Array, 
            context:ThreadContext, methodIndex:int, name:String, caller:IRubyObject, 
            callType:CallType , block:Block):IRubyObject {
        if (receiver == caller) callType = CallType.VARIABLE;
        
        return compilerCallMethodWithIndex(context, receiver, methodIndex, name, args, caller, 
                callType, block);
    }
    
    public static function doInvokeDynamic(receiver:IRubyObject , args:Array, 
            context:ThreadContext, name:String, caller:IRubyObject, callType:CallType , block:Block):IRubyObject {
        return compilerCallMethod(context, receiver, name, args, caller, callType, block);
    }
    
    public static function doInvokeDynamicIndexed(receiver:IRubyObject , args:Array, 
            context:ThreadContext, methodIndex:int, name:String, caller:IRubyObject, 
            callType:CallType , block:Block):IRubyObject {
        return compilerCallMethodWithIndex(context, receiver, methodIndex, name, args, caller, 
                callType, block);
    }

    /**
     * Used by the compiler to ease calling indexed methods, also to handle visibility.
     * NOTE: THIS IS NOT THE SAME AS THE SWITCHVALUE VERSIONS.
     */
    public static function compilerCallMethodWithIndex(context:ThreadContext, receiver:IRubyObject, methodIndex:int, name:String, args:Array, caller:IRubyObject, callType:CallType, block:Block):IRubyObject{
        var clazz: RubyClass =  receiver.getMetaClass();
        
        if (clazz.index != 0) {
            return clazz.invoke(context, receiver, methodIndex, name, args, callType, block);
        }
        
        return compilerCallMethod(context, receiver, name, args, caller, callType, block);
    }
    
    /**
     * Used by the compiler to handle visibility
     */
    public static function compilerCallMethod(context:ThreadContext, receiver:IRubyObject , name:String,
            args:Array, caller:IRubyObject, callType:CallType , block:Block):IRubyObject {
        //assert args != null;
        var method: DynamicMethod =  null;
        var rubyclass: RubyModule =  receiver.getMetaClass();
        method = rubyclass.searchMethod(name);
        
        if (method.isUndefined() || (!(name == "method_missing") && !method.isCallableFrom(caller, callType))) {
            return callMethodMissing2(context, receiver, method, name, args, caller, callType, block);
        }

        return method.call(context, receiver, rubyclass, name, args, block);
    }
    
    public static function callMethodMissing(context:ThreadContext, receiver:IRubyObject , method:DynamicMethod , name:String, methodIndex:int,
                                                args:Array, self:IRubyObject, callType:CallType , block:Block):IRubyObject {
        // store call information so method_missing impl can use it            
        context.setLastCallStatus(callType);            
        context.setLastVisibility(method.getVisibility());

        if (methodIndex == MethodIndex.METHOD_MISSING) {
            return method_missing(context, self, args, block);
        }

        var newArgs: Array = args.slice(0, args.length);
        newArgs.unshift(context.getRuntime().newSymbol(name));
        
        return receiver.callMethod4(context, "method_missing", newArgs, block);
    }

    public static function callMethodMissing2(context:ThreadContext, receiver:IRubyObject , method:DynamicMethod , name:String, 
                                                args:Array, self:IRubyObject, callType:CallType , block:Block):IRubyObject {
        // store call information so method_missing impl can use it            
        context.setLastCallStatus(callType);            
        context.setLastVisibility(method.getVisibility());

        if (name == "method_missing") {
            return method_missing(context, self, args, block);
        }

		var newArgs: Array = args.slice(0, args.length);
        newArgs.unshift(context.getRuntime().newSymbol(name))

        return receiver.callMethod4(context, "method_missing", newArgs, block);
    }

    public static function invoke(context:ThreadContext, self:IRubyObject, name:String):IRubyObject{
        return RuntimeHelpers.invoke5(context, self, name, RubyObject.NULL_ARRAY, null, Block.NULL_BLOCK);
    }
    public static function invoke2(context:ThreadContext, self:IRubyObject, name:String, arg:IRubyObject):IRubyObject{
        return RuntimeHelpers.invoke5(context, self, name, [arg], CallType.FUNCTIONAL, Block.NULL_BLOCK);
    }
    public static function invoke3(context:ThreadContext, self:IRubyObject, name:String, args:Array):IRubyObject{
        return RuntimeHelpers.invoke5(context, self, name, args, CallType.FUNCTIONAL, Block.NULL_BLOCK);
    }
    public static function invoke4(context:ThreadContext, self:IRubyObject, name:String, args:Array, block:Block):IRubyObject{
        return RuntimeHelpers.invoke5(context, self, name, args, CallType.FUNCTIONAL, block);
    }
    
    public static function invoke5(context:ThreadContext, self:IRubyObject, name:String, args:Array, callType:CallType, block:Block):IRubyObject{
        return self.getMetaClass().invoke3(context, self, name, args, callType, block);
    }
    
    public static function invoke6(context:ThreadContext, self:IRubyObject, name:String, arg:IRubyObject, callType:CallType, block:Block):IRubyObject{
        return self.getMetaClass().invoke3(context, self, name, [arg], callType, block);
    }
    
    public static function invokeAs(context:ThreadContext, asClass:RubyClass, self:IRubyObject, name:String, args:Array, callType:CallType, block:Block):IRubyObject{
        return asClass.invoke3(context, self, name, args, callType, block);
    }
    
    // Indexed versions are for STI, which has become more and more irrelevant
    public static function invoke7(context:ThreadContext, self:IRubyObject, methodIndex:int, name:String, args:Array):IRubyObject{
        return RuntimeHelpers.invoke8(context, self, methodIndex,name,args,CallType.FUNCTIONAL, Block.NULL_BLOCK);
    }
    public static function invoke8(context:ThreadContext, self:IRubyObject, methodIndex:int, name:String, args:Array, callType:CallType, block:Block):IRubyObject{
        return self.getMetaClass().invoke(context, self, methodIndex, name, args, callType, block);
    }
/* 
    public static function ensureRubyArray(value:IRubyObject):RubyArray{
        if (!(value instanceof RubyArray)) {
            value = RubyArray.newArray(value.getRuntime(), value);
        }
        return  RubyArray(value);
    }

    public static function ensureMultipleAssignableRubyArray(runtime:Ruby, value:IRubyObject, masgnHasHead:Boolean):RubyArray{
        if (!(value instanceof RubyArray)) {
            value = ArgsUtil.convertToRubyArray(runtime, value, masgnHasHead);
        }
        return  RubyArray(value);
    }
    
    public static function fetchClassVariable(context:ThreadContext, runtime:Ruby , 
            self:IRubyObject, name:String):IRubyObject {
        var rubyClass: RubyModule =  ASTInterpreter.getClassVariableBase(context, runtime);
   
        if (rubyClass == null) rubyClass = self.getMetaClass();

        return rubyClass.getClassVar(name);
    }
    
    public static function fastFetchClassVariable(context:ThreadContext, runtime:Ruby , 
            self:IRubyObject, internedName:String ):IRubyObject {
        var rubyClass: RubyModule =  ASTInterpreter.getClassVariableBase(context, runtime);
   
        if (rubyClass == null) rubyClass = self.getMetaClass();

        return rubyClass.fastGetClassVar(internedName);
    }
    
    public static function nullToNil(value:IRubyObject, runtime:Ruby):IRubyObject{
        return value != null ? value : runtime.getNil();
    }
    
    public static function prepareSuperClass(runtime:Ruby, rubyClass:IRubyObject):RubyClass{
        RubyClass.checkInheritable(rubyClass); // use the same logic as in EvaluationState
        return rubyClass;
    } 
    
    public static function prepareClassNamespace(context:ThreadContext, rubyModule:IRubyObject):RubyModule{
        if (rubyModule == null || rubyModule.isNil()) {
            rubyModule = context.getCurrentScope().getStaticScope().getModule();
            
            if (rubyModule == null) {
                throw context.getRuntime().newTypeError("no outer class/module");
            }
        }
        
        return rubyModule;
    }
    
    public static function setClassVariable(context:ThreadContext, runtime:Ruby , 
            self:IRubyObject, name:String, value:IRubyObject ):IRubyObject {
        var rubyClass: RubyModule =  ASTInterpreter.getClassVariableBase(context, runtime);
   
        if (rubyClass == null) rubyClass = self.getMetaClass();

        rubyClass.setClassVar(name, value);
   
        return value;
    }
    
    public static function fastSetClassVariable(context:ThreadContext, runtime:Ruby , 
            self:IRubyObject, internedName:String , value:IRubyObject ):IRubyObject {
        var rubyClass: RubyModule =  ASTInterpreter.getClassVariableBase(context, runtime);
   
        if (rubyClass == null) rubyClass = self.getMetaClass();

        rubyClass.fastSetClassVar(internedName, value);
   
        return value;
    }
    
    public static function declareClassVariable(context:ThreadContext, runtime:Ruby, self:IRubyObject, name:String, value:IRubyObject):IRubyObject{
        // FIXME: This isn't quite right; it shouldn't evaluate the value if it's going to throw the error
        var rubyClass: RubyModule =  ASTInterpreter.getClassVariableBase(context, runtime);
   
        if (rubyClass == null) throw runtime.newTypeError("no class/module to define class variable");
        
        rubyClass.setClassVar(name, value);
   
        return value;
    }
    
    public static function fastDeclareClassVariable(context:ThreadContext, runtime:Ruby, self:IRubyObject, internedName:String, value:IRubyObject):IRubyObject{
        // FIXME: This isn't quite right; it shouldn't evaluate the value if it's going to throw the error
        var rubyClass: RubyModule =  ASTInterpreter.getClassVariableBase(context, runtime);
   
        if (rubyClass == null) throw runtime.newTypeError("no class/module to define class variable");
        
        rubyClass.fastSetClassVar(internedName, value);
   
        return value;
    }
    */
    public static function handleArgumentSizes(context:ThreadContext, runtime:Ruby, given:int, required:int, opt:int, rest:int):void{
        if (opt == 0) {
            if (rest < 0) {
                // no opt, no rest, exact match
                if (given != required) {
                    throw runtime.newArgumentError("wrong # of arguments(" + given + " for " + required + ")");
                }
            } else {
                // only rest, must be at least required
                if (given < required) {
                    throw runtime.newArgumentError("wrong # of arguments(" + given + " for " + required + ")");
                }
            }
        } else {
            if (rest < 0) {
                // opt but no rest, must be at least required and no more than required + opt
                if (given < required) {
                    throw runtime.newArgumentError("wrong # of arguments(" + given + " for " + required + ")");
                } else if (given > (required + opt)) {
                    throw runtime.newArgumentError("wrong # of arguments(" + given + " for " + (required + opt) + ")");
                }
            } else {
                // opt and rest, must be at least required
                if (given < required) {
                    throw runtime.newArgumentError("wrong # of arguments(" + given + " for " + required + ")");
                }
            }
        }
    }
    /*
    public static function getLocalJumpTypeOrRethrow(re:RubyException):String{
        var exception: RubyException =  re.getException();
        var runtime: Ruby =  exception.getRuntime();
        if (runtime.fastGetClass("LocalJumpError").isInstance(exception)) {
            var jumpError: RubyLocalJumpError =  re.getException();

            var reason: IRubyObject =  jumpError.reason();

            return reason.asJavaString();
        }

        throw re;
    }
    
    public static function unwrapLocalJumpErrorValue(re:RubyException):IRubyObject{
        return (re.getException()).exit_value();
    }
    
    public static function processBlockArgument(runtime:Ruby, block:Block):IRubyObject{
        if (!block.isGiven()) {
            return runtime.getNil();
        }
        
        var blockArg:RubyProc;
        
        if (block.getProcObject() != null) {
            blockArg = block.getProcObject();
        } else {
            blockArg = runtime.newProc(Block.PROC, block);
            blockArg.getBlock().type = Block.PROC;
        }
        
        return blockArg;
    }
    
    public static function getBlockFromBlockPassBody(proc:IRubyObject, currentBlock:Block):Block{
        var runtime: Ruby =  proc.getRuntime();

        // No block from a nil proc
        if (proc.isNil()) return Block.NULL_BLOCK;

        // If not already a proc then we should try and make it one.
        if (!(proc instanceof RubyProc)) {
            proc = TypeConverter.convertToType(proc, runtime.getProc(), 0, "to_proc", false);

            if (!(proc instanceof RubyProc)) {
                throw runtime.newTypeError("wrong argument type "
                        + proc.getMetaClass().getName() + " (expected Proc)");
            }
        }

        // TODO: Add safety check for taintedness
        if (currentBlock != null && currentBlock.isGiven()) {
            var procObject: RubyProc =  currentBlock.getProcObject();
            // The current block is already associated with proc.  No need to create a new one
            if (procObject != null && procObject == proc) return currentBlock;
        }

        return ( proc).getBlock();
    }
    
    public static function backref(context:ThreadContext):IRubyObject{
        var backref: IRubyObject =  context.getCurrentFrame().getBackRef();
        
        if(backref instanceof RubyMatchData) {
            //(backref).use();
        }
        return backref;
    }
    
    public static function backrefLastMatch(context:ThreadContext):IRubyObject{
        var backref: IRubyObject =  context.getCurrentFrame().getBackRef();
        
        return RubyRegexp.last_match(backref);
    }
    
    public static function backrefMatchPre(context:ThreadContext):IRubyObject{
        var backref: IRubyObject =  context.getCurrentFrame().getBackRef();
        
        return RubyRegexp.match_pre(backref);
    }
    
    public static function backrefMatchPost(context:ThreadContext):IRubyObject{
        var backref: IRubyObject =  context.getCurrentFrame().getBackRef();
        
        return RubyRegexp.match_post(backref);
    }
    
    public static function backrefMatchLast(context:ThreadContext):IRubyObject{
        var backref: IRubyObject =  context.getCurrentFrame().getBackRef();
        
        return RubyRegexp.match_last(backref);
    }
    
    public static function callZSuper(runtime:Ruby, context:ThreadContext, block:Block, self:IRubyObject):IRubyObject{
        if (context.getFrameKlazz() == null) {
            var name: String =  context.getFrameName();
            throw runtime.newNameError("superclass method '" + name
                    + "' disabled", name);
        }
        
        // Has the method that is calling super received a block argument
        if (!block.isGiven()) block = context.getCurrentFrame().getBlock(); 
        
        return self.callSuper(context, context.getCurrentScope().getArgValues(), block);
    }
    
    public static function appendToObjectArray(array:Array, add:IRubyObject):Array{
        var newArray: Array =  new IRubyObject[array.length + 1];
        System.arraycopy(array, 0, newArray, 0, array.length);
        newArray[array.length] = add;
        return newArray;
    }
    
    public static function returnJump(result:IRubyObject, context:ThreadContext):IRubyObject{
        throw new JumpException.ReturnJump(context.getFrameJumpTarget(), result);
    }
    
    public static function breakJumpInWhile(bj:RubyException, aBlock:Block):IRubyObject{
        // JRUBY-530, while case
        if (bj.getTarget() == aBlock.getBody()) {
            bj.setTarget(null);
            
            throw bj;
        }

        return  bj.getValue();
    }
    
    public static function breakJump(value:IRubyObject):IRubyObject{
        throw new JumpException().BreakJump(null, value);
    }
    
    public static function breakLocalJumpError(runtime:Ruby, value:IRubyObject):IRubyObject{
        throw runtime.newLocalJumpError("break", value, "unexpected break");
    }
    
    public static function concatObjectArrays(array:Array, add:Array):Array{
        var newArray: Array =  new IRubyObject[array.length + add.length];
        System.arraycopy(array, 0, newArray, 0, array.length);
        System.arraycopy(add, 0, newArray, array.length, add.length);
        return newArray;
    }
    
    public static function isExceptionHandled(currentException:RubyException, exceptions:Array, runtime:Ruby, context:ThreadContext, self:IRubyObject):IRubyObject{
        for ( var i:int = 0; i < exceptions.length; i++) {
            if (!runtime.getModule().isInstance(exceptions[i])) {
                throw runtime.newTypeError("class or module required for rescue clause");
            }
            var result: IRubyObject =  exceptions[i].callMethod(context, "===", currentException);
            if (result.isTrue()) return result;
        }
        return runtime.getFalse();
    }
    
    public static function checkSuperDisabled(context:ThreadContext):void{
        var klazz: RubyModule =  context.getFrameKlazz();
        
        if (klazz == null) {
            var name: String =  context.getFrameName();
            throw context.getRuntime().newNameError("Superclass method '" + name
                    + "' disabled.", name);
        }
    }
    
    public static function ensureSuperBlock(given:Block, parent:Block):Block{
        if (!given.isGiven()) {
            return parent;
        }
        return given;
    }
    */
    public static function findImplementerIfNecessary(clazz:RubyModule, implementationClass:RubyModule):RubyModule{
        if (implementationClass != null && implementationClass.needsImplementer()) {
            // modules are included with a shim class; we must find that shim to handle super() appropriately
            return clazz.findImplementer(implementationClass);
        } else {
            // classes are directly in the hierarchy, so no special logic is necessary for implementer
            return implementationClass;
        }
    }

    /*    
    public static function createSubarray(input:RubyArray, start:int):RubyArray{
        return input.subseqLight(start, input.size() - start);
    }
    
    public static function createSubarray1(input:Array, runtime:Ruby, start:int):RubyArray{
        return RubyArray.newArrayNoCopy(runtime, input, start);
    }
    
    public static function isWhenTriggered(expression:IRubyObject, expressionsObject:IRubyObject, context:ThreadContext):RubyBoolean{
        var expressions: RubyArray =  ASTInterpreter.splatValue(context.getRuntime(), expressionsObject);
        for ( var j:int = 0,k:int = expressions.getLength(); j < k; j++) {
            var condition: IRubyObject =  expressions.eltInternal(j);

            if ((expression != null && condition.callMethod(context, MethodIndex.OP_EQQ, "===", expression)
                    .isTrue())
                    || (expression == null && condition.isTrue())) {
                return context.getRuntime().getTrue();
            }
        }
        
        return context.getRuntime().getFalse();
    }
    
    public static function setConstantInModule(module:IRubyObject, value:IRubyObject, name:String, context:ThreadContext):IRubyObject{
        return context.setConstantInModule(name, module, value);
    }
    
    public static function retryJump():IRubyObject{
        throw JumpException.RETRY_JUMP;
    }
    
    public static function redoJump():IRubyObject{
        throw JumpException.REDO_JUMP;
    }
    
    public static function redoLocalJumpError(runtime:Ruby):IRubyObject{
        throw runtime.newLocalJumpError("redo", runtime.getNil(), "unexpected redo");
    }
    
    public static function nextJump(value:IRubyObject):IRubyObject{
        throw new JumpException.NextJump(value);
    }
  
    public static function nextLocalJumpError(runtime:Ruby, value:IRubyObject):IRubyObject{
        throw runtime.newLocalJumpError("next", value, "unexpected next");
    }
    
    public static var MAX_SPECIFIC_ARITY_OBJECT_ARRAY: int =  5;
    
    public static function constructObjectArray(one:IRubyObject):Array{
        return [one];
    }
    
    public static function constructObjectArray1(one:IRubyObject, two:IRubyObject):Array{
        return [one, two];
    }
    
    public static function constructObjectArray2(one:IRubyObject, two:IRubyObject, three:IRubyObject):Array{
        return [one, two, three];
    }
    
    public static function constructObjectArray3(one:IRubyObject, two:IRubyObject, three:IRubyObject, four:IRubyObject):Array{
        return [one, two, three, four];
    }
    
    public static function constructObjectArray4(one:IRubyObject, two:IRubyObject, three:IRubyObject, four:IRubyObject, five:IRubyObject):Array{
        return [one, two, three, four, five];
    }
    
    public static var MAX_SPECIFIC_ARITY_HASH: int =  3;
    
    public static function constructHash(runtime:Ruby, key1:IRubyObject, value1:IRubyObject):RubyHash{
        var hash: RubyHash =  RubyHash.newHash(runtime);
        hash.fastASet(key1, value1);
        return hash;
    }
    
    public static function constructHash1(runtime:Ruby, key1:IRubyObject, value1:IRubyObject, key2:IRubyObject, value2:IRubyObject):RubyHash{
        var hash: RubyHash =  RubyHash.newHash(runtime);
        hash.fastASet(key1, value1);
        hash.fastASet(key2, value2);
        return hash;
    }
    
    public static function constructHash2(runtime:Ruby, key1:IRubyObject, value1:IRubyObject, key2:IRubyObject, value2:IRubyObject, key3:IRubyObject, value3:IRubyObject):RubyHash{
        var hash: RubyHash =  RubyHash.newHash(runtime);
        hash.fastASet(key1, value1);
        hash.fastASet(key2, value2);
        hash.fastASet(key3, value3);
        return hash;
    }
    
    public static function defineAlias(context:ThreadContext, newName:String, oldName:String):IRubyObject{
        var runtime: Ruby =  context.getRuntime();
        var module: RubyModule =  context.getRubyClass();
   
        if (module == null) throw runtime.newTypeError("no class to make alias");
   
        module.defineAlias(newName, oldName);
        module.callMethod(context, "method_added", runtime.newSymbol(newName));
   
        return runtime.getNil();
    }
    
    public static function getInstanceVariable(runtime:Ruby, self:IRubyObject, name:String):IRubyObject{
        var result: IRubyObject =  self.getInstanceVariables().getInstanceVariable(name);
        
        if (result != null) return result;
        
        runtime.getWarnings().warning("", "instance variable " + name + " not initialized");
        
        return runtime.getNil();
    }
    
    public static function fastGetInstanceVariable(runtime:Ruby, self:IRubyObject, internedName:String):IRubyObject{
        var result:IRubyObject;
        if ((result = self.getInstanceVariables().fastGetInstanceVariable(internedName)) != null) return result;
        
        runtime.getWarnings().warning("", "instance variable " + internedName + " not initialized");
        
        return runtime.getNil();
    }
    
    public static function negate(value:IRubyObject, runtime:Ruby):IRubyObject{
        if (value.isTrue()) return runtime.getFalse();
        return runtime.getTrue();
    }
    
    public static function stringOrNil(value:String, runtime:Ruby, nil:IRubyObject):IRubyObject{
        if (value == null) return nil;
        return RubyString.newString(runtime, value);
    }
    
    public static function preLoad(context:ThreadContext, varNames:Array):void{
        var staticScope: StaticScope =  new LocalStaticScope(null, varNames);
        staticScope.setModule(context.getRuntime().getObject());
        var scope: DynamicScope =  DynamicScope.newDynamicScope(staticScope);
        
        // Each root node has a top-level scope that we need to push
        context.preScopedBody(scope);
    }
    
    public static function postLoad(context:ThreadContext):void{
        context.postScopedBody();
    }
    
    public static function registerEndBlock(block:Block, runtime:Ruby):void{
        runtime.pushExitBlock(runtime.newProc(Block.LAMBDA, block));
    }
    
   public static function match3(regexp:RubyRegexp, value:IRubyObject, context:ThreadContext):IRubyObject{
        if (value instanceof RubyString) {
            return regexp.op_match(context, value);
        } else {
            return value.callMethod(context, "=~", regexp);
        }
    } */
    
    public static function getErrorInfo(runtime:Ruby):IRubyObject{
        return runtime.getGlobalVariables().getVal("$!");
    }
    
    public static function setErrorInfo(runtime:Ruby, error:IRubyObject):void{
        runtime.getGlobalVariables().setVal("$!", error);
    }

    public static function setLastLine(runtime:Ruby, context:ThreadContext, value:IRubyObject):void{
        context.getCurrentFrame().setLastLine(value);
    }

    public static function getLastLine(runtime:Ruby, context:ThreadContext):IRubyObject{
        return context.getCurrentFrame().getLastLine();
    }
/*
    public static function setBackref(runtime:Ruby, context:ThreadContext, value:IRubyObject):void{
        if (!value.isNil() && !(value instanceof RubyMatchData)) throw runtime.newTypeError(value, runtime.getMatchData());
        context.getCurrentFrame().setBackRef(value);
    }

    public static function getBackref(runtime:Ruby, context:ThreadContext):IRubyObject{
        var backref: IRubyObject =  context.getCurrentFrame().getBackRef();
        if (backref instanceof RubyMatchData) (backref).use0();
        return backref;
    }
    
    public static function preOpAsgnWithOrAnd(receiver:IRubyObject, context:ThreadContext, varSite:CallSite):IRubyObject{
        return varSite.call(context, receiver);
    }
    
    public static function postOpAsgnWithOrAnd(receiver:IRubyObject, value:IRubyObject, context:ThreadContext, varAsgnSite:CallSite):IRubyObject{
        return varAsgnSite.call(context, receiver, value);
    }
    
    public static function opAsgnWithMethod(context:ThreadContext, receiver:IRubyObject, arg:IRubyObject, varSite:CallSite, opSite:CallSite, opAsgnSite:CallSite):IRubyObject{
        var var0: IRubyObject =  varSite.call(context, receiver);
        var result: IRubyObject =  opSite.call(context, var0, arg);
        opAsgnSite.call(context, receiver, result);

        return result;
    }
    
    public static function opElementAsgnWithMethod(context:ThreadContext, receiver:IRubyObject, value:IRubyObject, elementSite:CallSite, opSite:CallSite, elementAsgnSite:CallSite):IRubyObject{
        var var0: IRubyObject =  elementSite.call(context, receiver);
        var result: IRubyObject =  opSite.call(context, var0, value);
        elementAsgnSite.call(context, receiver, result);

        return result;
    }
    
    public static function opElementAsgnWithMethod1(context:ThreadContext, receiver:IRubyObject, arg:IRubyObject, value:IRubyObject, elementSite:CallSite, opSite:CallSite, elementAsgnSite:CallSite):IRubyObject{
        var var0: IRubyObject =  elementSite.call(context, receiver, arg);
        var result: IRubyObject =  opSite.call(context, var0, value);
        elementAsgnSite.call(context, receiver, arg, result);

        return result;
    }
    
    public static function opElementAsgnWithMethod2(context:ThreadContext, receiver:IRubyObject, arg1:IRubyObject, arg2:IRubyObject, value:IRubyObject, elementSite:CallSite, opSite:CallSite, elementAsgnSite:CallSite):IRubyObject{
        var var0: IRubyObject =  elementSite.call(context, receiver, arg1, arg2);
        var result: IRubyObject =  opSite.call(context, var0, value);
        elementAsgnSite.call(context, receiver, arg1, arg2, result);

        return result;
    }
    
    public static function opElementAsgnWithMethod3(context:ThreadContext, receiver:IRubyObject, arg1:IRubyObject, arg2:IRubyObject, arg3:IRubyObject, value:IRubyObject, elementSite:CallSite, opSite:CallSite, elementAsgnSite:CallSite):IRubyObject{
        var var0: IRubyObject =  elementSite.call(context, receiver, arg1, arg2, arg3);
        var result: IRubyObject =  opSite.call(context, var0, value);
        elementAsgnSite.call(context, receiver, [arg1, arg2, arg3, result]);

        return result;
    }
    
    public static function opElementAsgnWithMethod4(context:ThreadContext, receiver:IRubyObject, args:Array, value:IRubyObject, elementSite:CallSite, opSite:CallSite, elementAsgnSite:CallSite):IRubyObject{
        var var0: IRubyObject =  elementSite.call(context, receiver);
        var result: IRubyObject =  opSite.call(context, var0, value);
        elementAsgnSite.call(context, receiver, appendToObjectArray(args, result));

        return result;
    }
    
    public static function opElementAsgnWithOrPartTwoOneArg(context:ThreadContext, receiver:IRubyObject, arg:IRubyObject, value:IRubyObject, asetSite:CallSite):IRubyObject{
        asetSite.call(context, receiver, arg, value);
        return value;
    }
    
    public static function opElementAsgnWithOrPartTwoTwoArgs(context:ThreadContext, receiver:IRubyObject, args:Array, value:IRubyObject, asetSite:CallSite):IRubyObject{
        asetSite.call(context, receiver, args[0], args[1], value);
        return value;
    }
    
    public static function opElementAsgnWithOrPartTwoThreeArgs(context:ThreadContext, receiver:IRubyObject, args:Array, value:IRubyObject, asetSite:CallSite):IRubyObject{
        asetSite.call(context, receiver, [args[0], args[1], args[2], value]);
        return value;
    }
    
    public static function opElementAsgnWithOrPartTwoNArgs(context:ThreadContext, receiver:IRubyObject, args:Array, value:IRubyObject, asetSite:CallSite):IRubyObject{
        var newArgs: Array =  new IRubyObject[args.length + 1];
        System.arraycopy(args, 0, newArgs, 0, args.length);
        newArgs[args.length] = value;
        asetSite.call(context, receiver, newArgs);
        return value;
    }
    */

    public static function method_missing(context:ThreadContext, recv:IRubyObject, args:Array, block:Block):IRubyObject{
        var runtime: Ruby =  recv.getRuntime();

        if (args.length == 0 || !(args[0] instanceof RubySymbol)) throw runtime.newArgumentError("no id given");
        
        var name: String =  args[0].asJavaString();
        var lastVis: Visibility =  context.getLastVisibility();
        var lastCallType: CallType =  context.getLastCallType();

        var format: String =  null;

        var noMethod: Boolean =  true; // NoMethodError

        if (lastVis == Visibility.PRIVATE) {
            format = "private method `%s' called for %s";
        } else if (lastVis == Visibility.PROTECTED) {
            format = "protected method `%s' called for %s";
        } else if (lastCallType == CallType.VARIABLE) {
            format = "undefined local variable or method `%s' for %s";
            noMethod = false; // NameError
        } else if (lastCallType == CallType.SUPER) {
            format = "super: no superclass method `%s'";
        }

        if (format == null) format = "undefined method `%s' for %s";

        var description: String =  null;
        
        if (recv.isNil()) {
            description = "nil";
        } else if (recv instanceof RubyBoolean && recv.isTrue()) {
            description = "true";
        } else if (recv instanceof RubyBoolean && !recv.isTrue()) {
            description = "false";
        } else {
            if (name == "inspect" || name == "to_s") {
                description = recv.anyToString().toString();
            } else {
                var d:IRubyObject;
                try {
                    d = recv.callMethod(context, "inspect");
                    if ( d.getMetaClass() == recv.getMetaClass() || 
                        (d instanceof RubyString && RubyString(d).length().getLongValue() > 65)) {
                        d = recv.anyToString();
                    }
                } catch (je:JumpException) {
                    d = recv.anyToString();
                }
                description = d.toString();
            }
        }
        if (description.length == 0 || (description.length > 0 && description.charAt(0) != '#')) {
            description = description + ":" + recv.getMetaClass().getRealClass().getName();            
        }
        
        var exArgs:Array = new Array;

        var arr: RubyArray =  runtime.newArray4(args[0], runtime.newString(description));
        var msg: RubyString =  runtime.newString(arr.toString());
        
        if (recv.isTaint()) msg.setTaint(true);

        exArgs[0] = msg;
        exArgs[1] = args[0];

        var exc:RubyClass;
        if (noMethod) {
            var NMEArgs:Array = args.slice(1, NMEArgs.length);
            exArgs[2] = runtime.newArrayNoCopy(NMEArgs);
            exc = runtime.fastGetClass("NoMethodError");
        } else {
            exc = runtime.fastGetClass("NameError");
        }
        
        throw new RaiseException(RubyException(exc.newInstance(context, exArgs, Block.NULL_BLOCK)));
    }    
    
}
}